package testBase;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import objects.User;
import objects.UserSet;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeClass;

import pages.InternalPage;
import pages.LoginPage;
import pages.Page;
import pages.UserManagmentPage;
import pages.UserPage;

import com.opera.core.systems.OperaDriver;

import fw.ApplicationManager;

public class TestBase {
	
	protected static ApplicationManager app;
	private Random rnd = new Random();


	
	protected WebDriver driver;
	protected WebDriverWait wait;
	protected StringBuffer verificationErrors = new StringBuffer();
	InputStream stream = null;
	InputStreamReader reader = null;
	protected Properties props = new Properties();
	private String configFile;
	private UserSet usersModel;
	protected boolean usersModelRebuild;
	
	
	
	
	@AfterSuite
	public void tearDown() throws Exception {
		stop();
	}
	
	@BeforeClass
	protected void getSeleniumDriver() {
		app = ApplicationManager.getInstance();
		
		if (configFile == null) {
			configFile = "appProperties";
		}
		InputStream stream;
		try {
			stream = new FileInputStream(new File(configFile));
			reader = new InputStreamReader(stream,"UTF-8");
			props.load(reader);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		String browser = props.getProperty("browser", "firefox");
		
		if (driver == null) {
			driver = getDriver(browser);
			driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
		}
	}	

	protected WebDriver getDriver(String browser) {

		if ("firefox".equals(browser)) {
			driver = new FirefoxDriver();
			wait = new WebDriverWait(driver, 10);
		} else if ("ie".equals(browser)) {
			driver = new InternetExplorerDriver();
			wait = new WebDriverWait(driver, 10);
		} else if ("chrome".equals(browser)) {
			driver = new ChromeDriver();
			wait = new WebDriverWait(driver, 10);
		} else if ("opera".equals(browser)) {
			driver = new OperaDriver();
			wait = new WebDriverWait(driver, 10);
		}
		return driver;
		
	}
	
	protected WebDriver stop() {
		driver.quit();
		return driver;
		
	}
	
	public <T> void assertThat(T actual, Matcher<T> matcher) {
		assertThat("", actual, matcher);
	}
	
	public <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
		if (!matcher.matches(actual)) {
			Description description = new StringDescription();
			description.appendText(reason)
				.appendText("\nExpected: ")
				.appendDescriptionOf(matcher)
				.appendText("\n     got: ")
				.appendValue(actual)
				.appendText("\n");
	        
			throw new java.lang.AssertionError(description.toString());
		}
	}
	
	public void testUIAndDBUserListConformsModel() {			
		UserSet modelList = getUIUsers();
		UserSet uiList = getUIUsers();
		assertThat(uiList,equalTo(modelList));
		//System.out.println(modelList);
		//System.out.println(uiList);
	}
	
	public void prepare(int i) {
		String language = props.getProperty("language", "english");
		logInByAdmin();
		selectLanguage(language);
		synchronizeUsers();
		prepareUsers(i);
	}
	
	private void selectLanguage(String language) {
		WebElement div = driver.findElement(By.className("languages"));
		WebElement languages = div.findElement(By.tagName("ul"));
		if ("english".equals(language)) {
			languages.findElement(By.cssSelector("a[href='./?lang=en_US']")).click();
		} 
		if ("nederlands".equals(language)) {
			languages.findElement(By.cssSelector("a[href='./?lang=nl_NL']")).click();
		}
		
	}

	public void prepareUsers(int i) {
		while (getUsers().size() < i) {
			User user = app.getUserDataGenerator().generateRandomUser();
			
			createUser(user);
		}
	}
	
	public void synchronizeUsers() {
		usersModelRebuild = true;
		getUsers();
		usersModelRebuild = false;
	}
	
	public UserSet getUsers() {
			usersModel = getUIUsers();
	return usersModel;
	}
	
	public UserSet getUIUsers() {
		HashSet<User> set = new HashSet<User>();
		Page.clickUserManagement();
		
		List<WebElement> entries = findUsers();
		for (WebElement entry : entries) {
			User user = getUserDetails(entry);
			set.add(user);
		} 
		UserSet users = new UserSet(set);
		return users;
	}
	
	private List<WebElement> findUsers() {
		wait.until(visibilityOfElementLocated(By.tagName("table")));
		WebElement tbody = UserManagmentPage.userTable.findElement(By.tagName("tbody"));
		List<WebElement> entries = tbody.findElements(By.tagName("tr"));
		
		return entries;
	}
	
	private User getUserDetails(WebElement entry) {
		WebElement td = entry.findElements(By.tagName("td")).get(0);
		String href = td.findElement(By.tagName("a")).getAttribute("href");
		String id = href.replace("http://localhost/php4dvd/?go=user&id=", "");
		String name = entry.findElements(By.tagName("td")).get(1).getText();
		String email = entry.findElements(By.tagName("td")).get(2).getText();
		String role = entry.findElements(By.tagName("td")).get(3).getText();
		
		User user = new User()
							.setName(name)
								.setEmail(email)
									.setId(id)
										.setRole(role);
		return user;
	}
	
	public void createRandomUser() {
		User user = app.getUserDataGenerator().generateRandomUser();
		createUser(user);
		usersModel = usersModel.withAdded(user);
	}
	
	public void removeRandomUser() {
		User user = app.getUserDataGenerator().generateRandomUser();
		userRemove(user);
		usersModel = usersModel.without(user);
	}
	
	public User userRemove(User user) {
		WebElement tr = UserManagmentPage.userTable.findElement(By.xpath("//a[@href='./?go=user&id=" + user.getId() + "']/../.."));
		tr.findElement(By.cssSelector("img[src='http://localhost/php4dvd/tpl/default/images/icons/16x16/delete.png']")).click();
		wait.until(alertIsPresent()).accept();
		return user;
	}

	public User getRandomRemovableUser() {
		User user;
		do {
			user = getRandomUser();
		} while (user.getName().equals("admin"));
		return user;
	 }

	public User getRandomUser() {
		return (User) getSetElement(getUsers().getStoredUsers(), rnd.nextInt(getUsers().size())); 
	}
	
	protected Object getSetElement(Set<?> objects, int toGet) {
		return objects.toArray()[toGet];
	}

	public void openBaseUrl() {
		driver.get("http://localhost/php4dvd/");
		initializationPageObgect();
	}

	private void initializationPageObgect() {
		PageFactory.initElements(driver, LoginPage.class);
		PageFactory.initElements(driver, InternalPage.class);
		PageFactory.initElements(driver, UserManagmentPage.class);
		PageFactory.initElements(driver, UserPage.class);
		PageFactory.initElements(driver, Page.class);
	}

	public void logInByAdmin() {
		openBaseUrl();
		Page.login();		
	}

	public User createUser(User user) {
		Page.clickUserManagement();
		Page.createRandomUser(user);
		return user;
	
	}	
	
	public void userModifacation(User user) {
		User newUser = app.getUserDataGenerator().generateRandomEmailAndRole();
		Page.clickUserManagement();
		Page.userModify(user, newUser);
		usersModel = usersModel.without(user).withAdded(newUser);		
	}
	
	public void logOut() {
		Page.logout();
		wait.until(alertIsPresent()).accept();
	}
	
}
